package com.mpgame.battle;

import java.util.Arrays;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.Collectors;

import com.mpgame.common.proto.*;
import com.mpgame.core.battle.BattleEngine;
import com.mpgame.core.model.RoundResult;
import com.mpgame.database.mysql.model.game.MonsterStore;
import com.mpgame.database.mysql.services.GameBattleDataSvc;
import com.mpgame.database.mysql.services.GameBattleDataSvc.GameMonsterLoadData;

public class BattleRoom {
    private final int roomId;
    private final Queue<BattleActionReq> actionQueue = new ConcurrentLinkedQueue<>();

    private volatile int account1Id, account2Id;
    private volatile boolean isAccount1Offline, isAccount2Offline;
    private volatile boolean isAccount1Ready, isAccount2Ready;
    private volatile boolean isAccount1FinishedAnim, isAccount2FinishedAnim;
    private volatile MonsterStore monster1, monster2;

    private boolean isBattleEnded;
    private final BattleEngine battieEngine;

    public BattleRoom(int roomId, int account1Id, int account2Id, MonsterStore monster1, MonsterStore monster2){
        this.roomId = roomId;
        this.account1Id = account1Id;
        this.account2Id = account2Id;
        this.monster1 = monster1;
        this.monster2 = monster2;

        GameBattleDataSvc gameBattleDataSvc = new GameBattleDataSvc();
        GameMonsterLoadData loadData = gameBattleDataSvc.loadMonsters(monster1, monster2);
        battieEngine = new BattleEngine(loadData.monster1, loadData.monster2);
    }
    public int getRoomId(){return roomId;}

    public void setReady(int accountId){
        if(accountId == account1Id) isAccount1Ready = true;
        else isAccount2Ready = true;
        
    }
    public void setPlayerOffline(int accountId){
        if(accountId == account1Id) isAccount1Offline = true;
        else isAccount2Offline = true;
    }
    public boolean hasPlayerOffline(){return isAccount1Offline || isAccount2Offline;}
    public int getOnlinePlayerId(){
        if(!isAccount1Offline) return account1Id;
        if(!isAccount2Offline) return account2Id;
        return -1;
    }
    public List<Integer> getPlayerIds(){return List.of(account1Id, account2Id);}
    public MonsterStore getMonsterStore(int accountId){return accountId == account1Id ? monster1 : monster2;}
    public MonsterStore getOpponentMonsterStore(int accountId){return accountId != account1Id ? monster1 : monster2;}
    public int getAccount1Id(){return account1Id;}
    public int getAccount2Id(){return account2Id;}
    public int getOpponentAccountId(int accountId){return accountId != account1Id ? account1Id : account2Id;}
    public boolean isAllReady(){return isAccount1Ready && isAccount2Ready;}
    public boolean isBattleEnded(){ return this.isBattleEnded; }

    public synchronized BattleActionRsp processAction(BattleActionReq req){
        if(isBattleEnded) return null;
        actionQueue.offer(req);
        // 双方都提交操作，开始处理回合
        if(actionQueue.size() >= 2){
            BattleActionReq reqFirst = actionQueue.poll();
            BattleActionReq reqSecond = actionQueue.poll();

            BattleActionReq req1 = reqFirst.getAccountId() == account1Id ? reqFirst : reqSecond;
            BattleActionReq req2 = reqSecond.getAccountId() == account2Id ? reqSecond : reqFirst;

            battieEngine.doRound(req1.getIndex(), req2.getIndex());
            RoundResult roundResult = battieEngine.getRoundResult();

            return buildBattleActionRspFromRoundResult(req1.getAccountId(), req2.getAccountId(), roundResult);
        }
        return null;
    }
    public synchronized boolean setFinishedAnim(int accountId){
        if(accountId == account1Id) isAccount1FinishedAnim = true;
        else isAccount2FinishedAnim = true;
        if(isAccount1FinishedAnim && isAccount2FinishedAnim){
            isAccount1FinishedAnim = false;
            isAccount2FinishedAnim = false;
            return true;
        }
        return false;
    }
    private BattleActionRsp buildBattleActionRspFromRoundResult(int account_id_1, int account_id_2, RoundResult roundResult){
        BattleActionRsp.Builder builder = BattleActionRsp.newBuilder().setAccount1Id(account_id_1).setAccount2Id(account_id_2);
        BattleActionResultMsg payload1 = buildBattleActionResultMsgFromRoundResult(account_id_1, roundResult);
        BattleActionResultMsg payload2 = buildBattleActionResultMsgFromRoundResult(account_id_2, roundResult);
        builder.setPayload1(payload1);
        builder.setPayload2(payload2);
        int winner_id = -1;
        if(roundResult.getWinner_idx() > 0){
            if(roundResult.getWinner_idx() == 1) winner_id = account_id_1;
            else winner_id = account_id_2;
            isBattleEnded = true;
        }
        builder.setWinnerId(winner_id);
        return builder.build();
    }
    private BattleActionResultMsg buildBattleActionResultMsgFromRoundResult(int account_id, RoundResult roundResult){
        boolean isOne = account_id == account1Id;
        BattleActionResultMsg.Builder msg = BattleActionResultMsg.newBuilder();
        msg.setDamageToMe(isOne ? roundResult.getDmg_to_mon1() : roundResult.getDmg_to_mon2());
        msg.setDamageToOpponent(isOne ? roundResult.getDmg_to_mon2() : roundResult.getDmg_to_mon1());
        msg.setMyCurrentHealth(isOne ? roundResult.getCurHealth_mon1() : roundResult.getCurHealth_mon2());
        msg.setOpponentCurrentHealth(isOne ? roundResult.getCurHealth_mon2() : roundResult.getCurHealth_mon1());
        msg.setMyMaxHealth(isOne ? roundResult.getMaxHealth_mon1() : roundResult.getMaxHealth_mon2());
        msg.setOpponentMaxHealth(isOne ? roundResult.getMaxHealth_mon2() : roundResult.getMaxHealth_mon1());
        msg.setIsMyCritical(isOne ? roundResult.is_crit_mon1() : roundResult.is_crit_mon2());
        msg.setIsOpponentCritical(isOne ? roundResult.is_crit_mon2() : roundResult.is_crit_mon1());
        msg.setIsMyMiss(isOne ? roundResult.is_miss_mon1() : roundResult.is_miss_mon2());
        msg.setIsOpponentCritical(isOne ? roundResult.is_miss_mon2() : roundResult.is_miss_mon1());
        msg.setSkill1CurrentPp(isOne ? roundResult.getSkill1pp_mon1() : roundResult.getSkill1pp_mon2());
        msg.setSkill2CurrentPp(isOne ? roundResult.getSkill2pp_mon1() : roundResult.getSkill2pp_mon2());
        msg.setSkill3CurrentPp(isOne ? roundResult.getSkill3pp_mon1() : roundResult.getSkill3pp_mon2());
        msg.setSkill4CurrentPp(isOne ? roundResult.getSkill4pp_mon1() : roundResult.getSkill4pp_mon2());
        msg.setCurrentRoundCount(roundResult.getCur_round_count());
        msg.setBattleLog(roundResult.getBattle_log());
        msg.addAllMyReinforceLevels(Arrays.stream(isOne ? roundResult.getReinforces_mon1() : roundResult.getReinforces_mon2()).boxed().collect(Collectors.toList()));
        msg.addAllOpponentReinforceLevels(Arrays.stream(isOne ? roundResult.getReinforces_mon2() : roundResult.getReinforces_mon1()).boxed().collect(Collectors.toList()));
        msg.setIsMyFirst(isOne ? roundResult.is_mon1_first() : !roundResult.is_mon1_first());
        msg.setMySkillType(isOne ? roundResult.getSkillType_mon1() : roundResult.getSkillType_mon2());
        msg.setOpponentSkillType(isOne ? roundResult.getSkillType_mon2() : roundResult.getSkillType_mon1());
        return msg.build();
    }
}
